/*
 * FindBugs - Find Bugs in Java programs
 * Copyright (C) 2006, University of Maryland
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package edu.umd.cs.findbugs.util;

import javax.annotation.meta.When;

import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.classfile.ClassDescriptor;
import edu.umd.cs.findbugs.classfile.DescriptorFactory;
import edu.umd.cs.findbugs.internalAnnotations.DottedClassName;
import edu.umd.cs.findbugs.internalAnnotations.SlashedClassName;

/**
 * Utility methods for working with class names.
 *
 * @author David Hovemeyer
 */
public abstract class ClassName {

	public static String toSignature(@SlashedClassName String className) {
		if (className.length() == 0)
			throw new IllegalArgumentException("classname can't be empty");
		if (className.charAt(0) == '[' || className.endsWith(";")) return className;
		return "L" + className + ";";
	}


	public static @CheckForNull @SlashedClassName String fromFieldSignature(String signature) {
		if (signature.charAt(0) != 'L') return null;
		return signature.substring(1, signature.length()-1);
	}

	/**
	 *
	 * @param signature bytecode notated type name
	 * @return for reference types: class name without bytecode characters, otherwise
	 * unchanged signature
	 */
	public static String fromSignature(String signature) {
		if (signature.charAt(0) == '[') {
			if (signature.charAt(signature.length() - 1) == ';') {
				// [Ljava.lang.String; or [[Ljava.lang.String;
				int start = 1;
				while (signature.charAt(start) == '[') {
					start++;
				}
				return signature.substring(start + 1, signature.length() - 1);
			} else {
				// [Z
				return signature; //signature.substring(start, signature.length());
			}
		}
		return signature;
	}

	/**
	 * Convert class name to slashed format.
	 * If the class name is already in slashed format,
	 * it is returned unmodified.
	 *
	 * @param className a class name
	 * @return the same class name in slashed format
	 */
	public static @SlashedClassName String toSlashedClassName(
			@SlashedClassName(when=When.UNKNOWN) String className) {
		if (className.indexOf('.') >= 0) {
			return DescriptorFactory.canonicalizeString(className.replace('.', '/'));
		}
		return className;
	}

	/**
	 * Convert class name to dotted format.
	 * If the class name is already in dotted format,
	 * it is returned unmodified.
	 *
	 * @param className a class name
	 * @return the same class name in dotted format
	 */
	public static @DottedClassName String toDottedClassName(@SlashedClassName(when=When.UNKNOWN) String className) {
		if (className.indexOf('/') >= 0) {
			return DescriptorFactory.canonicalizeString(className.replace('/', '.'));
		}
		return className;
	}

		
	/**
	 * extract the package name from a dotted class name.
	 * Package names are always in dotted format.
	 *
	 * @param className a dotted class name
	 * @return the name of the package containing the class
	 */
	public static @DottedClassName String extractPackageName(@DottedClassName String className) {
		int i = className.lastIndexOf('.');
		if (i < 0) return "";
		return className.substring(0, i);
	}
	public static @DottedClassName String extractSimpleName(@DottedClassName String className) {
		int i = className.lastIndexOf('.');
		if (i < 0) return className;
		return className.substring(i+1);
	}
	/**
	 * Return whether or not the given class name is valid.
	 *
	 * @param className a possible class name
	 * @return true if it's a valid class name, false otherwise
	 */
	public static boolean isValidClassName(String className) {
		// FIXME: should use a regex

		if (className.indexOf('(') >= 0) {
			return false;
		}
		return true;
	}

	/**
	 * Does a class name appear to designate an anonymous class?
	 * Only the name is analyzed. No classes are loaded or looked up.
	 *
	 * @param className  class name, slashed or dotted, fully qualified or unqualified
	 * @return true if className is the name of an anonymous class
	 */
	public static boolean isAnonymous(String className) {
		int i = className.lastIndexOf('$');
		if (i >= 0 && i + 1 < className.length()) {
			return Character.isDigit(className.charAt(i + 1));
		}
		return false;
	}

	/**
	 * Extract a slashed classname from a JVM classname or signature.
	 * 
	 * @param originalName JVM classname or signature
	 * @return a slashed classname 
	 */
	public static @SlashedClassName String extractClassName(String originalName) {
    	String name = originalName;
    	if (name.charAt(0) != '[' && name.charAt(name.length() - 1) != ';')
    		return name;
    	while (name.charAt(0) == '[')
    		name = name.substring(1);
    	if (name.charAt(0) == 'L' && name.charAt(name.length() - 1) == ';')
    		name = name.substring(1, name.length() - 1);
    	if (name.charAt(0) == '[') throw new IllegalArgumentException("Bad class name: " + originalName);
    	return name;
    }


	public static String extractPackagePrefix(String packageName, int count) {
        int dotsSeen = 0;
        int prefixLength = 0;
        while (dotsSeen < count) {
        	int p = packageName.indexOf('.', prefixLength);
        	if (p < 0) 
        		return packageName;
        	prefixLength = p+1;
        	dotsSeen++;
        }
        if (prefixLength == 0)
        	return "";
        return packageName.substring(0, prefixLength-1);
    }


	public static boolean matchedPrefixes(String[] classSearchStrings, @DottedClassName String className) {
    	String[] pp = classSearchStrings;
    	if (pp == null || pp.length == 0)
    		return true;
    
    	for (String p : pp)
    		if (p.length() > 0 && className.indexOf(p) >= 0)
    			return true;
    
    	return false;
    
    }
}
